#include "calque.h"

using namespace std;

Calque::Calque(int posX, int posY) :
    QImage()
{
    iniInfo();
    this->posX  = posX;
    this->posY  = posY;
    visible=true;
    opacite=100;
    nom = new QString("Calque");
}

Calque::Calque(int width, int height, Format format, int posX, int posY, int opacite) :
    QImage(width, height, format)
{
    this->opacite = opacite;
    // remplissage de blanc
    for(int i=0; i<height; i++){
        for(int j=0; j<width; j++){
            this->setPixel(j, i, qRgba(255, 255, 255, (255*opacite)/100));
        }
    }

    iniInfo();
    this->posX  = posX;
    this->posY  = posY;
    visible = true;
    nom = new QString("Calque");
}

Calque::Calque(const QString & fileName, const char * format ,int posX, int posY) :
    QImage(fileName, format)
{
    iniInfo();
    this->posX  = posX;
    this->posY  = posY;
    visible=true;
    opacite=100;
    nom = new QString("Calque");}

Calque::Calque(const char * fileName, const char * format, int posX, int posY) :
    QImage(fileName, format)
{
    iniInfo();
    this->posX  = posX;
    this->posY  = posY;
    visible=true;
    opacite=100;
    nom = new QString("Calque");
}


Calque::Calque(const Calque & image, int posX, int posY) :
    QImage((QImage)image)
{
    iniInfo();
    this->posX  = posX;
    this->posY  = posY;
    visible=true;
    opacite=100;
    nom = new QString("Calque");
}

Calque::Calque(QImage image, int posX, int posY) :
    QImage(image)
{
    iniInfo();
    this->posX  = posX;
    this->posY  = posY;
    visible=true;
    opacite=100;
    nom = new QString("Calque");
}


Calque::~Calque(){
    delete nom;
}


///////// GET / SET ///////////


bool Calque::isVisible() {
    return visible;
}

void Calque::setVisible(bool b) {
    visible = b;
}

std::vector<std::vector<int> >* Calque::getHistogrammeRgb() {
    return &histogrammeRgb;
}

std::vector<std::vector<double> >* Calque::getHistogrammeYuv() {
    return &histogrammeYuv;
}

QString* Calque::getNom(){
    return nom;
}

void Calque::setNom(QString n) {
    delete nom;
    nom = new QString(n);
}

void Calque::setPosition(int x, int y){
    this->posX  = x;
    this->posY  = y;
}

int Calque::getPosX(){
    return this->posX;
}

int Calque::getPosY(){
    return this->posY;
}


void Calque::setOpacite(int o) {
    opacite=o;
}


int Calque::getOpacite() {
    return opacite;
}



////////////////////////////////////



void Calque::iniInfo()
{
    histogrammeRgb.resize(3);
    for(size_t i = 0 ; i < 3 ; i++)
        histogrammeRgb[i].resize(256);

    histogrammeYuv.resize(3);
    for(size_t i=0; i<3; i++)
        histogrammeYuv[i].resize(256);

    updateHistogramme();
}

void Calque::updateInfo() {
    updateHistogramme();
}

void Calque::updateHistogramme(){
    this->razHistogramme();
    QRgb couleur;
    for(int i=0; i<this->height(); i++){
        for(int j=0; j<this->width(); j++){
            couleur = this->pixel(j,i);
            if(qAlpha(couleur) != 0) {
                histogrammeRgb[rouge][qRed(couleur)]++;
                histogrammeRgb[vert][qGreen(couleur)]++;
                histogrammeRgb[bleu][qBlue(couleur)]++;

                histogrammeYuv[Y][rgbToY(couleur)]++;

                int rec = recadrage(-111.18,111.18,0,255,rgbToU(couleur));
                histogrammeYuv[U][rec]++;

                rec = recadrage(-156.825,156.825,0,255,rgbToV(couleur));
                histogrammeYuv[V][rec]++;
            }
        }
    }
}

/////////////////////// RGB <=> YUV //////////////////////////

double Calque::rgbToY(QRgb rgb) {
    double res = (0.299* (double) qRed(rgb)
               + 0.587* (double) qGreen(rgb)
               + 0.114* (double) qBlue(rgb));
    return res;
}

double Calque::rgbToU(QRgb rgb) {
    double res = (- 0.147* (double)qRed(rgb)
               - 0.289* (double) qGreen(rgb)
               + 0.436* (double)qBlue(rgb));
    return res;
}

double Calque::rgbToV(QRgb rgb) {
    double res = ((0.615*(double) qRed(rgb))
               - (0.515*(double) qGreen(rgb))
               - (0.100*(double) qBlue(rgb)));
    return res;

}

QRgb Calque::yuvToRgb(double y, double u, double v) {
    int R = (int) (y + 1.140*v);
    int G = (int) (y - 0.395*u - 0.581*v);
    int B = (int) (y + 2.032*u);
    return qRgb(R,G,B);
}



void Calque::razHistogramme(){

    std::fill(histogrammeRgb[rouge].begin(), histogrammeRgb[rouge].end(),0);
    std::fill(histogrammeRgb[vert].begin(), histogrammeRgb[vert].end(),0);
    std::fill(histogrammeRgb[bleu].begin(), histogrammeRgb[bleu].end(),0);

    std::fill(histogrammeYuv[rouge].begin(), histogrammeYuv[rouge].end(),0);
    std::fill(histogrammeYuv[vert].begin(), histogrammeYuv[vert].end(),0);
    std::fill(histogrammeYuv[bleu].begin(), histogrammeYuv[bleu].end(),0);
}

int Calque::recadrage(double A, double B, double C, double D, double val){
    int res =(int)(((D-C)/(B-A))*val + C-((D-C)/(B-A))*A);
    return res;
}



void Calque::egaliserHistogramme(){

    double s_hist_eq[256]={0.0}, sum_of_hist[256]={0.0};
    long i, x, y, n;

    n =  height()*width();
    for (i=0;i<256;i++)  // pdf of image
    {
        s_hist_eq[i] = (double)histogrammeYuv[Y][i]/(double)n;
    }

    sum_of_hist[0] = s_hist_eq[0];
    for (i=1;i<256;i++)        // cdf of image
    {
        sum_of_hist[i] = sum_of_hist[i-1] + s_hist_eq[i];
    }

    int lum;
    double u, v;
    uint* line;
    for(y=0;y<height();y++){
        line = (uint*) scanLine(y);
        for(x=0;x<width();x++) {
            lum = rgbToY(line[x]);
            u = rgbToU(line[x]);
            v = rgbToV(line[x]);
            line[x] = yuvToRgb(sum_of_hist[lum]*255.0 ,u,v);

        }
    }
}

void Calque::recadrageHistogramme(int destMin, int destMax) {

    int orgMin=0, orgMax=0;

    for(int i=0; i<256; i++) {
        int nb = histogrammeYuv[Y][i];
        if(orgMin == 0 && nb != 0) {
            orgMin = i;
        }
        if(nb != 0 ) {
            orgMax = i;
        }
    }

    int histoTmp[256] = {0}; //contient les nouveaux indices

    //on fabrique l'histo recadré
    for(int i=orgMin; i<=orgMax; i++){
        int gris = recadrage(orgMin, orgMax, destMin, destMax,i);
        histoTmp[i] = gris;
    }

    uint* line;
    int lum;
    int u,v;
    for(int y=0;y<height();y++){
        line = (uint*) scanLine(y);
        for(int x=0;x<width();x++) {
            lum = rgbToY(line[x]);
            u = rgbToU(line[x]);
            v = rgbToV(line[x]);
            line[x] = yuvToRgb(histoTmp[lum] ,u,v);
        }
    }
}

Calque * Calque::copie(){
        Calque *copie = new Calque(*this,this->getPosX(),this->getPosY());
        return copie;
}
